class BaseSentinel extends Pawn
    abstract;

var() class<xPawnGibGroup> GibGroupClass;
var() Material DamageOverlay;
var() float DamageOverlayTime;
var() Sound DamageSounds[4];
var ShadowProjector PlayerShadow;
var() vector ShadowDir;

var() float cost,refControl;

var string Sname;
var tfpri pri;
var SentinelPlayerReplicationInfo spri;
var tfmenuconsole mcon;
var int myteam;
var bool brecycled;
var Controller InstigatorController;
var byte MuzFlashCount, LastFlashCount;

var bool bDied, bForceDeath;

var() vector CentreOffset; //Where the visual centre of the model is, for positioning things like the shield sphere.

var bool bIsInvasion;

var bool bWasBased;

replication
{
	reliable if(Role == ROLE_Authority)
		InstigatorController, bIsInvasion, Sname;

	unreliable if(Role == ROLE_Authority)
		MuzFlashCount;
}

simulated function PostBeginPlay()
{
	if(ControllerClass != None && Controller == None)
		Controller = spawn(ControllerClass);
}

function InitializeFor(Pawn Placer)
{
	if(Placer == None || Placer.Controller == None || Placer.Controller.PlayerReplicationInfo == None || Controller == None)
	{
		Destroy();
		return;
	}

	Instigator = Placer;
	InstigatorController = Placer.Controller;
	myteam = placer.PlayerReplicationInfo.Team.TeamIndex;
	Controller.Possess(self);
	spri = SentinelPlayerReplicationInfo(self.PlayerReplicationInfo);
	spri.Team.TeamIndex = myteam;
}

//Kill Sentinels when round resets in BR, otherwise they get forcibly moved to a playerstart.
function PlayTeleportEffect(bool bOut, bool bSound)
{
	bForceDeath = true;
	SetTimer(0.1, false);
}

event TakeDamage(int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, class<DamageType> DamageType)
{
  local playercontroller pc;
  local controller p;
  local int num, i;

    spri=SentinelPlayerReplicationInfo(self.PlayerReplicationInfo);

	if(Role < ROLE_Authority || bTearOff || Health <= 0)
		return;

	//Ion Cannon are wimpy, so give them a little help.
	if(DamageType == class'DamTypeIonBlast')
		Damage *= 2;

	if(EventInstigator != None && Controller != None && Controller.SameTeamAs(EventInstigator.Controller) && TeamGame(Level.Game) != None)
		Damage *= TeamGame(Level.Game).FriendlyFireScale;

	Health -= Damage;

    if(damage>0)
    {
	    num=fmin(damage/3,20);
        if(level.bdropdetail) num/=2;

        for(i=0; i<num; i++)
	    {
            //if ( EffectIsRelevant(hitlocation,false) )
            //{
                spawn(class'TaskForces.tfStationSparks',,,hitlocation,rotator(Normal(hitlocation - location)));
            //}
        }

        for ( P = Level.ControllerList; P!=None; P=P.nextController )
        {
            pc=playercontroller(p);

            if(pc!=none && p.PlayerReplicationInfo.Team.TeamIndex==myteam)
               pc.ReceiveLocalizedMessage(class'TaskForces.tfSentinelDmgMsg',damage,,,self);
        }
    }

	if(Health <= 0)
	{
		if(EventInstigator != None)
			Died(EventInstigator.Controller, DamageType, HitLocation);
		else
			Died(None, DamageType, HitLocation);
	}

	else
	{
		PlayHit(Damage, EventInstigator, HitLocation, DamageType, Momentum);

		if(Controller != None)
			Controller.NotifyTakeHit(EventInstigator, HitLocation, Damage, DamageType, Momentum);
    }
	if(EventInstigator!=none)
        {
            if(EventInstigator.playerreplicationinfo.team.teamindex==myteam)
            {
                EventInstigator.playerreplicationinfo.score-=float(damage)/200;
                tfpri(EventInstigator.playerreplicationinfo).SubSkill(-float(damage)/10);
            }
            else
            {
                EventInstigator.playerreplicationinfo.score+=float(damage)/200;
                tfpri(EventInstigator.playerreplicationinfo).AddSkill(float(damage)/10);
            }
        }
}

function setrecyclestate()
{
brecycled=true;
}

//Fix for ubiquitous no-instigator bug.
function RenewInstigator()
{
	if(InstigatorController == None)
		Died(None, None, Location);

	if((Instigator == None || Instigator == self) && InstigatorController.Pawn != None)
		Instigator = InstigatorController.Pawn;
}

function Died(Controller Killer, class<DamageType> DamageType, vector HitLocation)
{
    local controller c;

    if(killer!=none && killer.PlayerReplicationInfo!=none)
    {
        if(killer.PlayerReplicationInfo.Team.TeamIndex!=myteam)
        {
            //SCORE************************
            killer.PlayerReplicationInfo.Score+=cost/100;
            tfpri(killer.PlayerReplicationInfo).AddSkill(cost/10);
            tfpri(killer.PlayerReplicationInfo).AddRes(cost/10);
            RenewInstigator();
            HurtRadius(Default.Health * 0.125, (CollisionRadius + CollisionHeight) * 3, class'DamTypeSentinelExplosion', 10000, Location);
        }
        else //destroyed own building
        {
            //SCORE************************
            if(!brecycled)
            {
                killer.PlayerReplicationInfo.Score-=cost/100;
                tfpri(killer.PlayerReplicationInfo).SubSkill(-cost/50);
                tfpri(killer.PlayerReplicationInfo).SubRes(-cost/10);
            }
            else
            {
                killer.PlayerReplicationInfo.Score-=cost/200;
                tfpri(killer.PlayerReplicationInfo).SubSkill(-cost/100);
                tfpri(killer.PlayerReplicationInfo).AddRes(cost/2);
            }
        }
    }

    for ( c = Level.ControllerList; c!=None; c=c.nextController )
    {
        if(playercontroller(c)!=none && c.PlayerReplicationInfo.team.TeamIndex==myteam)
            playercontroller(c).ReceiveLocalizedMessage(class'TaskForces.TFSLoseMsg',,killer.playerreplicationinfo,,self);
    }

	if(Controller != None)
	{
		Controller.PawnDied(self);
		PlayDying(DamageType, HitLocation);
	}
	else
		Destroy();
}

//Damage telefraggers.
event EncroachedBy(Actor Other)
{
	if(Pawn(Other) != None && Vehicle(Other) == None)
	{
		gibbedBy(Other);
		Other.TakeDamage(Default.Health * 0.125, Instigator, Location, normal(Other.Location - Location) * 10000, class'DamTypeSentinelExplosion');
	}
}

simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
	if(bDied)
		return;

	bTearOff = true;
	LifeSpan = 0.1;

	if(Level.NetMode != NM_DedicatedServer)
	{
		PlaySound(sound'WeaponSounds.BExplosion3',, 2.5*TransientSoundVolume);
		Spawn(class'RocketExplosion',,, Location, rotator(vect(0, 0, -1)));
		Spawn(class'ExplosionCrap',,, Location, rotator(vect(0, 0, -1)));
		Spawn(class'RocketMark', self,, Location + vect(0, 0, -46), rotator(vect(0, 0, -1)));
		SpawnGibs();
	}

	bDied = true;
}

function JumpOffPawn()
{
	Velocity += (100 + CollisionRadius) * VRand();
	Velocity.Z = 200 + CollisionHeight;
	SetPhysics(PHYS_Falling);
	bNoJumpAdjust = true;
}

event BaseChange()
{
	if(Controller == None || bWasBased)
		Destroy();

	Super.BaseChange();

	if(Base != None)
	{
		SetCollision(true, true, true);
		bCollideWorld = false; //Prevents vehicles from pushing Sentinels and stops Sentinels based on vehicles from sliding off.
		SetPhysics(PHYS_None);
		if(Skins[0]!=none)
		{
    		Skins[0] = Default.Skins[0];
    		RepSkin = Skins[0];
		}

		bWasBased = true;

		if(bActorShadows && class'xPawn'.Default.bPlayerShadows && Level.NetMode != NM_DedicatedServer)
		{
			PlayerShadow = Spawn(class'ShadowProjector',Self,'',Location);
			PlayerShadow.ShadowActor = self;
			PlayerShadow.bBlobShadow = class'xPawn'.Default.bBlobShadow;
			PlayerShadow.LightDirection = Normal(ShadowDir);
			PlayerShadow.LightDistance = 320;
			PlayerShadow.MaxTraceDistance = 350;
			PlayerShadow.InitShadow();
		}

		//Fix for Sentinel getting based before it is initialized.
		if(BaseSentinelController(Controller).sent == None)
			BaseSentinelController(Controller).sent = self;
	}
	else
		bCollideWorld = true;
}

event PhysicsVolumeChange(PhysicsVolume NewVolume)
{
	if(NewVolume.bWaterVolume)
		Died(None, None, Location);
}

//So Sentinels can't pick up the flag (D'oh!).
simulated function bool IsPlayerPawn()
{
	return false;
}

//Can't place two Sentinels on top of each other.
event bool EncroachingOn(actor Other)
{
	if(Other.IsA('Sentinel'))
		Destroy();

	return false;
}

//Will be destroyed if placed under a mover.
function PlayMoverHitSound()
{
	TakeDamage(250, None, Location, vect(0, 0, 0), class'Crushed');
}

simulated singular function Touch(Actor Other)
{
	if(Other == None)
		return;

	//Force grenades and bioglobs to explode when they hit a Sentinel.
	if(Other.IsA('Grenade'))
		Projectile(Other).Explode(Other.Location, Normal(Other.Velocity));
	else if(Other.IsA('BioGlob'))
		Projectile(Other).BlowUp(Other.Location);
	else if(Other.IsA('TransBeacon') && VSize(Other.Velocity) < 500) //Make telefragging more difficult.
		Other.Velocity *= 3.0;
}

simulated event Destroyed()
{
    if(level.game != none) tfgri(level.game.gamereplicationinfo).control[myteam]-=refControl;
	if(PlayerShadow != None)
		PlayerShadow.Destroy();

	super.Destroyed();
}

simulated function SpawnGibs()
{
        SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Calf));
        SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Forearm));
        SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Hand));
        SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Head));
	SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Torso));
        SpawnGiblet(GibGroupClass.static.GetGibClass(EGT_Upperarm));
}

simulated function SpawnGiblet(class<Gib> GibClass)
{
	local Gib Giblet;
	local rotator GibRotation;
	local vector GibLocation;

	Giblet = Spawn(GibClass);

	if(Giblet == None)
		return;

	GibLocation = Location;
	GibLocation.Y -= 56;
	GibLocation.Y += FRand() * 112;
	Giblet.SetLocation(GibLocation);

	GibRotation.Pitch = FRand() * 65536;
	GibRotation.Yaw = FRand() * 65536;
	GibRotation.Roll = FRand() * 65536;

	Giblet.Velocity = vector(GibRotation) * FRand() * 512.0;
}

defaultproperties
{
     Skins[0]=none
     HealthMax=1000.000000
     bCanJump=False
     bCanWalk=False
     bCanDoubleJump=False
     SightRadius=10000.000000
     MaxFallSpeed=400.000000
     BaseEyeHeight=0.000000
     EyeHeight=0.000000
     Health=1000
     GibGroupClass=Class'XEffects.xBotGibGroup'
     ControllerClass=class'taskforces.BaseSentinelController'
     bActorShadows=True
     bStasis=False
     mesh=SkeletalMesh'Cannons.FloorCannon'
     AmbientGlow=64
     bCollideWhenPlacing=True
     bDestroyInPainVolume=True
     bCanTeleport=False
     CollisionRadius=48.000000
     CollisionHeight=32.000000
     bUseCylinderCollision=True
     bBlockKarma=True
     bRotateToDesired=False
     Mass=300.000000
     Sname="Sentinel"
     cost=400
     refControl=200
}